package com.zhuangjie.idworker.helper;

import com.zhuangjie.idworker.properties.SnowflakeHelperProperties;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Component;

import java.util.Arrays;
import java.util.Collections;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

@Component
@Slf4j
public class ServiceNoHelper {
    @Autowired(required = false)
    private RedisTemplate redisTemplate;

    private static final String DEFAULT_PREFIX = "Snowflake::";

    private static final int MAX_LENGTH = 32;

    public String ASSEMBLE_PREFIX = null;

    @Autowired
    private SnowflakeHelperProperties snowflakeHelperProperties;

    /**
     * 获取服务编号
     * 看key是否存在
     * 存在：遍历查询空号从而得到服务号
     * 不存在：创建，将0的位置设置为1,并返回0
     *
     * @param dateCenterId
     * @param moduleName            模块名称
     * @param expirationTimeSeconds 失效（单位：秒）
     * @return int
     */
    public Long  getServiceNo(Integer dateCenterId, String moduleName, Integer expirationTimeSeconds) {
        // 组装总前缀
        this.ASSEMBLE_PREFIX = DEFAULT_PREFIX + dateCenterId+"::"+moduleName+"::";
        String luaScript =
                "local prefix = KEYS[1] " +
                "local items = redis.call('keys', prefix .. '*') " +
                "if (next(items) ~= nil) then " +
                    "for i = 0, ARGV[1] - 1 do "+
                        "if redis.call('EXISTS', prefix .. i) == 0 then "+
                            "redis.call('set',prefix .. i, 1) "+
                            "redis.call('EXPIRE',prefix .. i,ARGV[2]) "+
                            "return i "+
                        "end "+
                    "end "+
                "else " +
                    "redis.call('set',prefix .. 0, 1) "+
                    "redis.call('EXPIRE',prefix .. 0,ARGV[2]) "+
                    "return 0 " +
                "end "+
                "return -1 ";

        return  (Long)redisTemplate.execute(new DefaultRedisScript<>(luaScript, Long.class),
                Arrays.asList(this.ASSEMBLE_PREFIX), MAX_LENGTH,expirationTimeSeconds);
    }

    /**
     * 尝试恢复持有状态
     *
     * @param workerId 工作人员id
     * @return boolean
     * @throws ExecutionException   执行异常
     * @throws InterruptedException 中断异常
     * @throws TimeoutException     超时异常
     */
    public boolean tryStateRecovery(long workerId)  {
        String key = ASSEMBLE_PREFIX+workerId;
        int expirationTimeSeconds = snowflakeHelperProperties.getExpirationTimeSeconds();
        log.info("idWorker: 正在尝试恢复服务号持有状态！");
        String luaScript =
        "if redis.call('SETNX',KEYS[1],1) == 1 then "+
            "redis.call('EXPIRE',KEYS[1],ARGV[1]) "+
            "return 1 " +
        "else "+
             "return 0 "+
        "end ";
        CompletableFuture<Boolean> future = CompletableFuture.supplyAsync(() ->
                redisTemplate.execute(new DefaultRedisScript<>(luaScript, Long.class),
                        Collections.singletonList(key),expirationTimeSeconds).equals(1L));
        try {
            return future.get(2, TimeUnit.SECONDS);
        } catch (Exception e) {
            return false;
        }
    }

}
